using System;
using System.Collections;
using FM_Mono;
using UnityEngine;

public struct UpdateTimeInfo
{
    public int frameCount;
    public float timeScale;
    public float smoothDeltaTime;
    public float fixedDeltaTime;
    public float fixedUnscaledDeltaTime;
    public float unscaledDeltaTime;
    public float fixedUnscaledTime;
    public float unscaledTime;
    public float fixedTime;
    public float deltaTime;
    public float timeSinceLevelLoad;
    public float time;
    public float realtimeSinceStartup;  // 为了某些不高频取最新时间的场合，替换RealTimer.elapsedSeconds，
}

public class UniqueMonoEventListener : MonoBehaviour
{
    private static UniqueMonoEventListener _instance;

    [AcquireOnce]
    public static UniqueMonoEventListener instance
    {
        get
        {
            if (_instance == null)
            {
                GameObject go = new GameObject("UniqueMonoEventListener");
                _instance = go.AddComponent<UniqueMonoEventListener>();
            }
            return _instance;
        }
    }
    public Action<UpdateTimeInfo> scriptOnStart;
    public Action<UpdateTimeInfo> scriptOnFixedUpdate;
    public Action<UpdateTimeInfo> scriptOnUpdate;
    public Action<UpdateTimeInfo> scriptOnLateUpdate;
    public Action<bool> scriptOnApplicationFocus;
    public Action<bool> scriptOnApplicationPause;
    public Action scriptOnApplicationQuit;
    public Action scriptOnEndOfFrame;
    public Action<string, string> scriptOnNativePluginMessage;
    public UpdateTimeInfo currentUpdateTimeInfo { get; private set; }

    public Action onApplicationQuit;

    public static void ClearAll()
    {
        if (_instance)
        {
            _instance.DoClearAll();
            _instance = null;
        }
    }

    private void DoClearAll()
    {
        Action tempScriptOnAppQuit = scriptOnApplicationQuit;
        scriptOnStart = null;
        scriptOnFixedUpdate = null;
        scriptOnUpdate = null;
        scriptOnLateUpdate = null;
        scriptOnApplicationFocus = null;
        scriptOnApplicationPause = null;
        scriptOnApplicationQuit = null;
        scriptOnEndOfFrame = null;
        scriptOnNativePluginMessage = null;
        if (tempScriptOnAppQuit != null)
        {
            tempScriptOnAppQuit();
        }
    }

    void Awake ()                   
    { 
        if (_instance == null)
        {
            _instance = this;
            UnityEngine.Object.DontDestroyOnLoad(this.gameObject);
            RefreshUpdateTimeInfo();
            this.StartCoroutine(LoopDispatchEndOfFrame());
        }
        else
        {
            Destroy(this.gameObject);
        }
    }

    IEnumerator LoopDispatchEndOfFrame()
    {
        var action = new WaitForEndOfFrame();
        while(true)
        {
            yield return action;
            if (scriptOnEndOfFrame != null)
            {
                scriptOnEndOfFrame();
            }
        }
    }

    UpdateTimeInfo RefreshUpdateTimeInfo()
    {
        currentUpdateTimeInfo = new UpdateTimeInfo{
            frameCount = Time.frameCount, 
            timeScale = Time.timeScale, 
            smoothDeltaTime = Time.smoothDeltaTime,
            fixedDeltaTime = Time.fixedDeltaTime,
            fixedUnscaledDeltaTime = Time.fixedUnscaledDeltaTime,
            unscaledDeltaTime = Time.unscaledDeltaTime,
            fixedUnscaledTime = Time.fixedUnscaledTime,
            unscaledTime = Time.unscaledTime,
            fixedTime = Time.fixedTime,
            deltaTime = Time.deltaTime,
            timeSinceLevelLoad = Time.timeSinceLevelLoad,
            time = Time.time,
            realtimeSinceStartup = Time.realtimeSinceStartup,
        };
        return currentUpdateTimeInfo;
    }

    void Start()
    {
        scriptOnStart?.Invoke(RefreshUpdateTimeInfo());
    }

    void FixedUpdate ()      
    {
        scriptOnFixedUpdate?.Invoke(RefreshUpdateTimeInfo());
    }
    
    void Update ()    
    {
        scriptOnUpdate?.Invoke(RefreshUpdateTimeInfo());
    }
    
    void LateUpdate ()   
    {
        scriptOnLateUpdate?.Invoke(RefreshUpdateTimeInfo());
    }
    
    /// <summary>
    /// Callback sent to all game objects when the player gets or loses focus.
    /// </summary>
    /// <param name="focusStatus">The focus state of the application.</param>
    void OnApplicationFocus(bool focusStatus)
    {
        if (scriptOnApplicationFocus != null)
        {
            scriptOnApplicationFocus(focusStatus);
        }
    }

    /// <summary>
    /// Callback sent to all game objects when the player pauses.
    /// </summary>
    /// <param name="pauseStatus">The pause state of the application.</param>
    void OnApplicationPause(bool pauseStatus)
    {
        if (scriptOnApplicationPause != null)
        {
            scriptOnApplicationPause(pauseStatus);
        }
    }

    /// <summary>
    /// Callback sent to all game objects before the application is quit.
    /// </summary>
    void OnApplicationQuit()
    {
        if (scriptOnApplicationQuit != null)
        {
            scriptOnApplicationQuit();
        }

        if (onApplicationQuit != null)
        {
            onApplicationQuit();
        }
    }

    void OnNativePluginMessage(string mesaageRaw)
    {
        if (scriptOnNativePluginMessage != null && !string.IsNullOrEmpty(mesaageRaw))
        {
            NativePluginMessage message = JsonUtility.FromJson<NativePluginMessage>(mesaageRaw);
            scriptOnNativePluginMessage(message.name, message.content);
        }
    }

    private void OnDestroy()
    {
        scriptOnStart = null;
        scriptOnFixedUpdate = null;
        scriptOnUpdate = null;
        scriptOnLateUpdate = null;
        scriptOnApplicationFocus = null;
        scriptOnApplicationPause = null;
        scriptOnApplicationQuit = null;
        scriptOnEndOfFrame = null;
        scriptOnNativePluginMessage = null;
    }
}
